#include <ne.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <system/GlutApplication.h>

int    Width = 640;               /* Width of window */
int    Height = 480;              /* Height of window */
size_t c = GL_LUMINANCE;
ne::Engine *pEngine = 0;
ne::GlutApplication *pApplication;
ne::FontPtr font;
ne::FontPtr normalFont;
ne::FontPtr monoFont;
ne::FontPtr lightFont;
int trans = 0;

void keyboard(unsigned char key, int x, int y);
void redraw(void);
void resize(int width, int height);
void init(const char *filename, size_t size);

int main(int argc, char *argv[]) /* I - Command-line arguments */
{
    pApplication = new ne::GlutApplication();
    glutReshapeFunc(resize);
    glutDisplayFunc(redraw);
    glutKeyboardFunc(keyboard);
    //glutFullScreen();

    pEngine = pApplication->engine;
    if (!pEngine || !pEngine->isReady())
    {
        delete pApplication;
        return 0;
    }

    if (argc <= 1)
    {
        init("../font/PEPSI_pl.ttf", 12);
    }
    else if (argc == 2)
    {
        init(argv[1], 12);
    }
    else
    {
        init(argv[1], strtoul(argv[2], 0, 0));
    }
    glutMainLoop();

    delete pApplication;

    return 0;
}

void testFreeType(const char *filename)
{
    FT_Library library;
    FT_Init_FreeType(&library);

    FT_Face face;
    FT_New_Face(library, filename, 0, &face);
    FT_Select_Charmap(face, FT_ENCODING_UNICODE);
    FT_Set_Char_Size(face, (FT_F26Dot6)(12 << 6), 0, 72, 0);
    std::cout << "==========\n";
    std::cout << "Num Faces: " << face->num_faces << std::endl;
    std::cout << "Face Index: " << face->face_index << std::endl;
    std::cout << "Face Flags: " << face->face_flags << std::endl;
    std::cout << "Style Flags: " << face->style_flags << std::endl;
    std::cout << "Num Glyphs: " << face->num_glyphs << std::endl;
    std::cout << "Family Name: " << face->family_name << std::endl;
    std::cout << "Style Name: " << face->style_name << std::endl;
    std::cout << "Num Fixed Sizes: " << face->num_fixed_sizes << std::endl;
    /*
    for (int i=0; i<face->num_fixed_sizes; ++i)
    {
        std::cout << "BitmapSize[" << i << "]" << std::endl;
        std::cout << "    Height: " << face->available_sizes[i].height << std::endl;
        std::cout << "    Width: " << face->available_sizes[i].width << std::endl;
        std::cout << "    Size: " << face->available_sizes[i].size << std::endl;
        std::cout << "    X PPEM: " << face->available_sizes[i].x_ppem << std::endl;
        std::cout << "    Y PPEM: " << face->available_sizes[i].y_ppem << std::endl;
    }
    std::cout << "==========\n";
    */
    std::cout << "Num Charmaps: " << face->num_charmaps << std::endl;
    /*
    for (int i=0; i<face->num_charmaps; ++i)
    {
        std::cout << "CharMap[" << i << "]" << std::endl;
        std::cout << "Encoding: " << face->charmaps[i]->encoding << std::endl;
        std::cout << "Platform ID: " << face->charmaps[i]->platform_id << std::endl;
        std::cout << "Encoding ID: " << face->charmaps[i]->encoding_id << std::endl;
    }
    std::cout << "==========\n";
    */
    std::cout << "Units Per EM: " << face->units_per_EM << std::endl;
    std::cout << "Ascender: " << face->ascender << std::endl;
    std::cout << "Descender: " << face->descender << std::endl;
    std::cout << "Height: " << face->height << std::endl;
    std::cout << "Max Advance Width: " << face->max_advance_width << std::endl;
    std::cout << "Max Advance Height: " << face->max_advance_height << std::endl;
    std::cout << "Underline Position: " << face->underline_position << std::endl;
    std::cout << "Underline Thickness: " << face->underline_thickness << std::endl;
    std::cout << "==========\n";

    std::cout << "Face->bbox: " << std::endl;
    std::cout << "    X Min: " << face->bbox.xMin << std::endl;
    std::cout << "    X Max: " << face->bbox.xMax << std::endl;
    std::cout << "    Y Min: " << face->bbox.yMin << std::endl;
    std::cout << "    Y Max: " << face->bbox.yMax << std::endl;
    std::cout << "==========\n";

    FT_UInt gIndex;
    FT_ULong charCode = FT_Get_First_Char(face, &gIndex);
    int charNum = 0;

    while (gIndex != 0)
    {
        FT_Load_Char(face, charCode, FT_LOAD_RENDER | FT_LOAD_TARGET_LIGHT);
        if (face->glyph->bitmap.pitch > 0) ++charNum;
        /*
        std::cout << "Code[" << gIndex << "] = " << charCode << std::endl;
        std::cout << "Face->glyph: " << std::endl;
        std::cout << "    LinearHoriAdvance: " << face->glyph->linearHoriAdvance << std::endl;
        std::cout << "    LinearVertAdvance: " << face->glyph->linearVertAdvance << std::endl;
        std::cout << "    Bitmap_left: " << face->glyph->bitmap_left << std::endl;
        std::cout << "    Bitmap_top: " << face->glyph->bitmap_top << std::endl;
        std::cout << "    Num SubGlyphs: " << face->glyph->num_subglyphs << std::endl;
        std::cout << "Face->glyph->bitmap: " << std::endl;
        std::cout << "    Bitmap Rows: " << face->glyph->bitmap.rows << std::endl;
        std::cout << "    Bitmap Width: " << face->glyph->bitmap.width << std::endl;
        std::cout << "    Bitmap Pitch: " << face->glyph->bitmap.pitch << std::endl;
        std::cout << "Face->glyph->metrics: " << std::endl;
        std::cout << "    Width: " << face->glyph->metrics.width << std::endl;
        std::cout << "    Height: " << face->glyph->metrics.height << std::endl;
        std::cout << "    HoriBearingX: " << face->glyph->metrics.horiBearingX << std::endl;
        std::cout << "    HoriBearingY: " << face->glyph->metrics.horiBearingY << std::endl;
        std::cout << "    HoriAdvance: " << face->glyph->metrics.horiAdvance << std::endl;
        std::cout << "    VertBearingX: " << face->glyph->metrics.vertBearingX << std::endl;
        std::cout << "    VertBearingY: " << face->glyph->metrics.vertBearingY << std::endl;
        std::cout << "    VertAdvance: " << face->glyph->metrics.vertAdvance << std::endl;
        std::cout << "Face->size->metrics: " << std::endl;
        std::cout << "    X PPEM: " << face->size->metrics.x_ppem << std::endl;
        std::cout << "    Y PPEM: " << face->size->metrics.y_ppem << std::endl;
        std::cout << "    X Scale: " << face->size->metrics.x_scale << std::endl;
        std::cout << "    Y Scale: " << face->size->metrics.y_scale << std::endl;
        std::cout << "    Ascender: " << face->size->metrics.ascender << std::endl;
        std::cout << "    Descender: " << face->size->metrics.descender << std::endl;
        std::cout << "    Height: " << face->size->metrics.height << std::endl;
        std::cout << "    Max Advance: " << face->size->metrics.max_advance << std::endl;
        std::cout << "==========\n";
        */
        charCode = FT_Get_Next_Char(face, charCode, &gIndex);
    }

    std::cout << "========== Total: " << charNum << "\n";

    FT_Done_Face(face);
    FT_Done_FreeType(library);
}

void init(const char *filename, size_t size)
{
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    normalFont = pEngine->getFontManager()->createTrueTypeFont("PEPSI_Normal", filename, size, ne::FHT_NORMAL);
    normalFont->load();

    lightFont = pEngine->getFontManager()->createTrueTypeFont("PEPSI_Light", filename, size, ne::FHT_LIGHT);
    lightFont->load();

    monoFont = pEngine->getFontManager()->createTrueTypeFont("PEPSI_Mono", filename, size, ne::FHT_MONO);
    monoFont->load();

    font = normalFont;

    testFreeType(filename);
}

void keyboard(unsigned char key, int x, int y)
{
    if (key == '1' || key == '!')
    {
        c = GL_RED;
    }
    else if (key == '2' || key == '@')
    {
        c = GL_GREEN;
    }
    else if (key == '3' || key == '#')
    {
        c = GL_BLUE;
    }
    else if (key == '4' || key == '$')
    {
        c = GL_LUMINANCE;
    }
    else if (key == 'n' || key == 'N')
    {
        font = normalFont;
    }
    else if (key == 'm' || key == 'M')
    {
        font = monoFont;
    }
    else if (key == 'l' || key == 'L')
    {
        font = lightFont;
    }
    if (key == 0x1b)
    {
        exit(0);
    }
}

void redraw(void)
{
    glShadeModel(GL_SMOOTH);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glColor3f(1.0, 0.0, 0.0);
    glEnable(GL_BLEND);

    if (font.notNull())
    {
        glPushMatrix();
            float posX = -0.5f;
            float posY = 0.5f;
            float w = 2.0f * (float)(2 + font->getFontWidth())/Width;
            float h = 2.0f * (float)(2 + font->getFontHeight())/Height;

            ne::TexturePtr pFT = font->getTexture();
            glEnable(pFT->getType());
            pFT->createRenderTexture();
            glBindTexture(pFT->getType(), pFT->getSource());
            glColor3f(0.0f, 0.5f, 1.0f);
            float px = (1.0f * pFT->getWidth()) / Width;
            float py = (1.0f * pFT->getHeight()) / Height;
            glBegin(GL_TRIANGLES);
                glTexCoord2f(0.0f, 1.0f);
                glVertex3f(-px, -py, 0.0f);
                glTexCoord2f(1.0f, 1.0f);
                glVertex3f(px, -py, 0.0f);
                glTexCoord2f(1.0f, 0.0f);
                glVertex3f(px, py, 0.0f);

                glTexCoord2f(0.0f, 1.0f);
                glVertex3f(-px, -py, 0.0f);
                glTexCoord2f(1.0f, 0.0f);
                glVertex3f(px, py, 0.0f);
                glTexCoord2f(0.0f, 0.0f);
                glVertex3f(-px, py, 0.0f);
            glEnd();

            std::string str = "FreeType Test Application : ";
            for (ne::uint32_t i=0; i<0xFF; ++i)
            {
                str += char(i);
            }
            str += "中文测试《核引擎》Rockie X.Lee";
            ne::Unicode text(str);

            trans = (trans + 1) % 256;
            for (size_t i=0; i<text.length(); ++i)
            {
                float c = float(i)/256.0f;
                glColor3f(1.0f - c, float(std::abs(256-trans))/256.0f, c);

                ne::Glyph *glyph = font->getGlyph(text[i]);
                if (glyph)
                {
                    ne::Vector4 coord(posX + 2.0f * glyph->horiBearingX / Width,
                                      posY + 2.0f * (glyph->horiBearingY - glyph->height) / Height,
                                      posX + 2.0f * (glyph->horiBearingX + glyph->width) / Width,
                                      posY + 2.0f * glyph->horiBearingY / Height);
                    glBegin(GL_TRIANGLES);
                        glTexCoord2f(glyph->texCoords[0].x, glyph->texCoords[0].y);
                        glVertex3f(coord.x, coord.y, 0.0f);
                        glTexCoord2f(glyph->texCoords[1].x, glyph->texCoords[0].y);
                        glVertex3f(coord.z, coord.y, 0.0f);
                        glTexCoord2f(glyph->texCoords[1].x, glyph->texCoords[1].y);
                        glVertex3f(coord.z, coord.w, 0.0f);

                        glTexCoord2f(glyph->texCoords[0].x, glyph->texCoords[0].y);
                        glVertex3f(coord.x, coord.y, 0.0f);
                        glTexCoord2f(glyph->texCoords[1].x, glyph->texCoords[1].y);
                        glVertex3f(coord.z, coord.w, 0.0f);
                        glTexCoord2f(glyph->texCoords[0].x, glyph->texCoords[1].y);
                        glVertex3f(coord.x, coord.w, 0.0f);
                    glEnd();
                    posX = coord.z;
                }
                else
                {
                    posX += w;
                }

                if (posX >= 0.5f)
                {
                    posX = -0.5f;
                    posY -= h;
                }
            }
            glDisable(pFT->getType());
        glPopMatrix();
    }
    glutSwapBuffers();
    glutPostRedisplay();
}

void resize(int width, int height)
{
    /* Save the new width and height */
    Width  = width;
    Height = height;

    /* Reset the viewport... */
    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
}

